/*************************************************
 * 
 * Copyright by Bruno Eberhard
 * 
 * Created on 30.07.2005
 *
 *************************************************/
package fightplan.ui;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;

import fightplan.bindx.GObservableList;
import fightplan.core.FPExceptionHandler;

@SuppressWarnings("serial")
public class BeanListTableModel<E> extends AbstractTableModel implements TableModel, ListDataListener {

	private List<E> list;
	private GObservableList<E> observedList;
	private String[] properties;
	private String[] columnNames;
	private Class<?>[] columnClasses;
	private Set<String> readOnlyColumns;

	public BeanListTableModel(List<E> list, String[] properties, String[] columnNames) {
		this(list, properties, columnNames, null);
	}

	public BeanListTableModel(List<E> list, String[] properties, String[] columnNames, Class<?>[] columnClasses) {
		this.list = list;
		this.properties = properties;
		this.columnNames = columnNames;
		this.columnClasses = columnClasses;
		this.readOnlyColumns = new TreeSet<String>();
		if (list instanceof GObservableList) {
			observedList = (GObservableList<E>) list;
			observedList.addListDataListener(this);
		}
	}

	public void setList(List<E> list) {
		if (observedList != null) {
			observedList.removeListDataListener(this);
		}
		this.list = list;
		if (list instanceof GObservableList) {
			observedList = (GObservableList<E>) list;
			observedList.addListDataListener(this);
		} else {
			observedList = null;
		}
		fireTableDataChanged();
	}

	public List<E> getList() {
		return list;
	}

	public void addElement(E element) {
		list.add(element);
		if (observedList == null)
			fireTableRowsInserted(list.size() - 1, list.size() - 1);
	}

	public void addElement(int pos, E element) {
		list.add(pos, element);
		if (observedList == null)
			fireTableRowsInserted(list.size() - 1, list.size() - 1);
	}

	public void removeElement(E element) {
		int pos = list.indexOf(element);
		list.remove(element);
		if (observedList == null)
			fireTableRowsDeleted(pos, pos);
	}

	public void setColumnReadOnly(int index) {
		readOnlyColumns.add(columnNames[index]);
	}

	// TableModel

	public int getColumnCount() {
		return properties.length;
	}

	public int getRowCount() {
		return list != null ? list.size() : 0;
	}

	public Object getValueAt(int rowIndex, int columnIndex) {
		Object rowObject = list.get(rowIndex);
		return getPropertyValue(rowObject, properties[columnIndex]);
	}

	@Override
	public String getColumnName(int column) {
		return columnNames[column];
	}

	//

	@Override
	public Class<?> getColumnClass(int columnIndex) {
		if (columnClasses != null) {
			return columnClasses[columnIndex];
		} else {
			return super.getColumnClass(columnIndex);
		}
	}

	@Override
	public boolean isCellEditable(int rowIndex, int columnIndex) {
		return !readOnlyColumns.contains(columnNames[columnIndex]);
	}

	@Override
	public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
		Object rowObject = list.get(rowIndex);
		setPropertyValue(rowObject, properties[columnIndex], aValue);
	}

	static Object getPropertyValue(Object object, String property) {
		if (property == null || property.length() == 0)
			return object;
		if (property.indexOf(".") > -1) {
			int pos = property.lastIndexOf(".");
			String left = property.substring(0, pos);
			property = property.substring(pos + 1);
			object = getPropertyValue(object, left);
		}
		if (object == null)
			return null;
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
			PropertyDescriptor propertyDescriptor[] = beanInfo.getPropertyDescriptors();
			for (int i = 0; i < propertyDescriptor.length; i++) {
				if (propertyDescriptor[i].getName().equals(property)) {
					Method readMethod = propertyDescriptor[i].getReadMethod();
					return readMethod.invoke(object, new Object[0]);
				}
			}
			return " - ";
		} catch (Exception x) {
			x.printStackTrace();
			return "Exception";
		}
	}

	static void setPropertyValue(Object object, String property, Object value) {
		if (property == null)
			return; // no error, can happen for special tableEditors
		if (property.indexOf(".") > -1) {
			int pos = property.lastIndexOf(".");
			String left = property.substring(0, pos);
			property = property.substring(pos + 1);
			object = getPropertyValue(object, left);
		}
		if (object == null)
			return;
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
			PropertyDescriptor propertyDescriptor[] = beanInfo.getPropertyDescriptors();
			for (int i = 0; i < propertyDescriptor.length; i++) {
				if (propertyDescriptor[i].getName().equals(property)) {
					Method writeMethod = propertyDescriptor[i].getWriteMethod();
					writeMethod.invoke(object, new Object[] { value });
				}
			}
			return;
		} catch (Exception x) {
			FPExceptionHandler.instance().handle(x);
		}
	}

	public void intervalAdded(ListDataEvent e) {
		fireTableRowsInserted(e.getIndex0(), e.getIndex1());
	}

	public void intervalRemoved(ListDataEvent e) {
		fireTableRowsDeleted(e.getIndex0(), e.getIndex1());
	}

	public void contentsChanged(ListDataEvent e) {
		fireTableRowsUpdated(e.getIndex0(), e.getIndex1());
	}

}
